#ifndef HE3DB_LOGINDEX_H
#define HE3DB_LOGINDEX_H

#include "access/xlog.h"
#include "common/hashfn.h"
#include "port/atomics.h"
#include "storage/lockdefs.h"
#include "storage/lwlock.h"
#include "storage/shmem.h"
#include "storage/s_lock.h"
#include "storage/buf_internals.h"
#include "utils/pg_lsn.h"

#define LOG_INDEX_MEM_ITEM_SEG_LSN_NUM          10
#define LOG_INDEX_MEM_TBL_SEG_NUM           4096
#define LOG_INDEX_MEM_TBL_PAGE_NUM          (LOG_INDEX_MEM_TBL_SEG_NUM/2)
#define LOG_INDEX_TABLE_INVALID_ID          0
#define LOG_INDEX_TBL_INVALID_SEG           0

#define LOG_INDEX_MEM_TBL_STATE_FREE        (0x00)
#define LOG_INDEX_MEM_TBL_STATE_ACTIVE      (0x01)
#define LOG_INDEX_MEM_TBL_STATE_INACTIVE    (0x02)
#define LOG_INDEX_MEM_TBL_STATE_FLUSHED     (0x04)

#define LOG_INDEX_MEM_TBL_HASH_PAGE(tag) \
	(tag_hash(tag, sizeof(BufferTag)) % LOG_INDEX_MEM_TBL_PAGE_NUM)

#define LOG_INDEX_SAME_TABLE_LSN_PREFIX(table, lsn)   ((table)->meta.prefix_lsn == ((lsn) >> 32))

#define LOG_INDEX_MEM_TBL_SET_PREFIX_LSN(table, lsn) \
	{ \
		(table)->meta.prefix_lsn = ((lsn) >> 32) ; \
	}
#define LOG_INDEX_INSERT_LSN_INFO(lsn_seg, number, lsn) \
    { \
        (lsn_seg)->suffix_lsn[(number)] = ((lsn << 32) >> 32); \
    }
#define LOG_INDEX_COMBINE_LSN(table, suffix) \
	((((XLogRecPtr)((table)->meta.prefix_lsn)) << 32) | (suffix))

// metadata of log index mem table; size:37
typedef struct LogIndexMemMeta
{
    uint64              id;
    pg_atomic_uint32    state;
    uint16              page_free_head; // free location for LogIndexMemItemHead
    uint16              lsn_free_head; // free location for LogIndexMemItemSeg
    XLogRecPtr          min_lsn;
    XLogRecPtr          max_lsn;
    uint32              prefix_lsn;
    slock_t             meta_lock;
} LogIndexMemMeta;

// log index value, prefix of page head; size: 20+2+2+1=25
typedef struct LogIndexMemItemHead
{
    BufferTag           tag;
    uint16              next_item;
    uint16              next_seg;
    uint16              tail_seg;
    slock_t             head_lock;
} LogIndexMemItemHead;

// save page suffix lsn; size: 2+1+4*10=43
typedef struct LogIndexMemItemSeg
{
    uint16              prev_seg;
    uint16              next_seg;
    uint8               number;
    uint32              suffix_lsn[LOG_INDEX_MEM_ITEM_SEG_LSN_NUM];
} LogIndexMemItemSeg;

// log index mem table; size: 37+25*2048+43*4096+2*2048=231461≈226kB
typedef struct LogIndexMemTBL
{
    LogIndexMemMeta              meta;
    uint16                       hash[LOG_INDEX_MEM_TBL_PAGE_NUM];
    LogIndexMemItemHead          page_head[LOG_INDEX_MEM_TBL_PAGE_NUM];
    LogIndexMemItemSeg           seg_item[LOG_INDEX_MEM_TBL_SEG_NUM];
} LogIndexMemTBL;

// list of log index mem tables
typedef struct LogIndexMemList
{
    uint64                       table_start_index; // first mem_table index, will change by remove unless inactive table
    uint64                       active_table_index; // current mem_table index
    uint64                       table_cap;
    //slock_t                      lock;
    LogIndexMemTBL               mem_table[FLEXIBLE_ARRAY_MEMBER];
} LogIndexMemList;

// lsn listNode
// typedef struct LsnNode {
//     XLogRecPtr lsn;
//     struct LsnNode * next;
// } LsnNode;

typedef union He3DBBufTag{
    BufferTag tag;
    XLogRecPtr lsn;
} He3DBBufTag;

// page tag listNode
typedef struct TagNode {
    He3DBBufTag tag;
    struct TagNode * next;
} TagNode;

extern int he3db_logindex_mem_size;
extern Size He3dbLogIndexShmemSize(void);
extern uint64 GetMemTblSize(void);
extern void He3dbLogIndexTblListInit(void);
extern void InsertLogIndexByPage(const BufferTag *page, XLogRecPtr lsn);
extern void CleanLogIndexByPage(XLogRecPtr consistLsn);
extern LsnNode *GetLogIndexByPage(const BufferTag *page, XLogRecPtr start_lsn, XLogRecPtr end_lsn);
extern void FreeLsnNode(LsnNode *head);
extern TagNode *GetBufTagByLsnRange(XLogRecPtr start_lsn, XLogRecPtr end_lsn);
extern void FreeTagNode(TagNode *head);
extern bool CheckBufTagExistByLsnRange(const BufferTag *page, XLogRecPtr start_lsn, XLogRecPtr end_lsn);
extern void He3DBGetLogindexStats(uint64 *memtable_total, uint64 *memtable_used, uint64 *memtable_active_index,
                                  uint64 *memtable_start_index, uint64 *page_total);
#endif							/* HE3DB_LOGINDEX_H */